
#include <sys/types.h>
#include <linux/mm.h>
#include <errno.h>
#include <linux/kernel.h>
#include <linux/sched.h>
#include <unistd.h>
#include <fcntl.h>
//#include <head.h>
static shm_t _shm_head = {0, 0, 0, 0, NULL};
static shm_t *shm_head = &_shm_head;

int sys_shmget(key_t key,size_t size, int shmflg){
    // check if exist
    shm_t *p = shm_head->next;
    while (p!=NULL){
        if(p->key==key){
            return p->id;
            }
        p = p->next;
    }
    // not found
    if (size > PAGE_SIZE)
    {
        errno = EINVAL;
        return -1;
    }
    p = malloc(sizeof(shm_t));
    p->key = key;
    p->id = (int)key;//保证key和id一一对应即可
    p->paddr = get_free_page();
    if(!p->paddr) {
        errno = ENOMEM;
        return -1;
    }
    p->size = size;
    // insert
    p->next = shm_head->next;
    shm_head->next = p;
    return p->id;
}

void *sys_shmat(int shmid,const void *shmaddr,int shmflg){
    shm_t *p = shm_head->next;
    while (p!=NULL){
        if(p->id == shmid)
            break;
        p = p->next;
    }
    if(!p){
        errno = EINVAL;
        return -1;  
    }
    // use current->brk + current->start_code
    unsigned long address = current->brk + current->start_code;
    if(!put_page(p->paddr,address))
        return -1;  
    current->brk = current->brk + PAGE_SIZE;
    return address = address - current->start_code;//返回段内偏移地址
}

int sys_debug_paging(pid_t pid, void *address, const char *logPath){
    typedef unsigned long addr_t;
    int fd;
    addr_t linearBaseAddr,addr,paddr;
    //寻找pid对应的进程,from sys_kill
    struct task_struct **p = NR_TASKS + task;
    while (--p > &FIRST_TASK) {
		if (*p && (*p)->pid == pid)
            break;
	}
    //进程不存在
    if(p < &FIRST_TASK) return -1;
    //定义输出流
    if(logPath==NULL) fd = STDOUT_FILENO;
    else fd = open(logPath,O_WRONLY | O_CREAT | O_APPEND,0666);
    //计算ds基地址
    addr_t a = gdt[(*p)->tss.ldt >> 3].a;
    addr_t b = gdt[(*p)->tss.ldt >> 3].b;
    addr_t ldt_addr = ((b >> 24)<<24) + ((b&0xff) << 16) + (a>>16);
    addr_t desc_ds_a = *((addr_t *)ldt_addr + 2*((*p)->tss.ds>>3)); //低四字节
    addr_t desc_ds_b = *((addr_t *)ldt_addr + 2*((*p)->tss.ds>>3) + 1); //高四字节
    linearBaseAddr = ((desc_ds_b >> 24)<<24) + ((desc_ds_b&0xff) << 16) + (desc_ds_a>>16);

    if(address!=NULL){
        addr = linearBaseAddr + (addr_t)address;
        //拆分地址
        //|--dir--|--index--|--off--|
        //|31...22|21.....12|11....0|
        addr_t pg_dir_index = (addr_t)(addr>>22);
        addr_t pg_table_index = (addr_t)((addr>>12) & 0x3ff);
        addr_t offset = (addr_t)(addr & 0xfff);
        addr_t pg_table_addr = pg_dir[pg_dir_index] & 0xffff000;
        addr_t pg_addr = *((addr_t *)pg_table_addr + pg_table_index) & 0xffff000;
        paddr = pg_addr + (addr_t)address;
        fprintk(fd,"Pid: %d Task: %d linearBaseAddress: 0x%p\n\n", pid, (int)(p - task),linearBaseAddr);
        fprintk(fd,"dirIndex\t\ttableIndex\t\toffset\n");
        fprintk(fd,"%p\t\t%p\t\t%p\n\n",pg_dir_index,pg_table_index,offset);
        fprintk(fd,"tableAddress\t\tpageAddress\t\tphysicalAddress\n");
        fprintk(fd,"%p\t\t%p\t\t%p\n\n",pg_table_addr,pg_addr,paddr);
        fprintk(fd,"----------------------------------------------------------------\n");
        if(fd!=STDOUT_FILENO)
            close(fd);
        return 0;
    }
    else{
        //输出全部页目录信息，遍历pg_dir,查看所有最后一位P=1的页
        int i,j,count =0;
        addr_t table[1024];
        fprintk(fd,"Pid: %d Task: %d linearBaseAddress: 0x%p\n\n", pid, (int)(p - task),linearBaseAddr);
        fprintk(fd,"Index\ttableAddress\n");
        for(i=(linearBaseAddr>>22);i<1024;i++){
            if(pg_dir[i]&0x1){
                addr_t pg_table_addr = pg_dir[i] & 0xffff000;
                //该表项插入待查看数组
                table[count++] = pg_table_addr;
                fprintk(fd,"%x\t%p\n",i,pg_table_addr);
            }
        }
        fprintk(fd,"\n");
        //输出全部页表信息
        for(i=0;i<count;i++){
            addr_t pg_table_addr = table[i];
            fprintk(fd,"Table %p:\n",pg_table_addr);
            fprintk(fd,"\t\tIndex\tPageAddress\n");
            for(j=0;j<1024;j++){
                if(!(*((addr_t *)pg_table_addr + j) & 0x1))continue;
                addr_t pg_addr = *((addr_t *)pg_table_addr + j) & 0xffff000;
                fprintk(fd,"\t\t%x\t%p\n",j,pg_addr);
            }
            fprintk(fd,"\n");
        }
        fprintk(fd,"----------------------------------------------------------------\n");
        if(fd!=STDOUT_FILENO)
            close(fd);
        return 0;
    }
}
